import { CancellationToken, CustomDocument, CustomDocumentBackup, CustomDocumentBackupContext, CustomDocumentContentChangeEvent, CustomDocumentEditEvent, CustomDocumentOpenContext, CustomEditorProvider, Event, Uri, WebviewPanel, workspace } from 'vscode';
import { Disposable } from './dispose';

interface IBizDocumentEdit {
  readonly color: string;
  readonly stroke: ReadonlyArray<[number, number]>;
}

interface IBizDocumentDelegate {
  getFileData(): Promise<Uint8Array>;
}

export class IBizDocument extends Disposable implements CustomDocument {

  private readonly _uri: Uri;

  private _documentData: Uint8Array;
  private _edits: Array<IBizDocumentEdit> = [];
  private _savedEdits: Array<IBizDocumentEdit> = [];

  private readonly _delegate: IBizDocumentDelegate;

  private constructor(
    uri: Uri,
    initialContent: Uint8Array,
    delegate: IBizDocumentDelegate
  ) {
    super();
    this._uri = uri;
    this._documentData = initialContent;
    this._delegate = delegate;
  }

  static async create(
    uri: Uri,
    backupId: string | undefined,
    delegate: IBizDocumentDelegate,
  ): Promise<IBizDocument | PromiseLike<IBizDocument>> {
    // If we have a backup, read that. Otherwise read the resource from the workspace
    const dataFile = typeof backupId === 'string' ? Uri.parse(backupId) : uri;
    const fileData = await IBizDocument.readFile(dataFile);
    return new IBizDocument(uri, fileData, delegate);
  }

  private static async readFile(uri: Uri): Promise<Uint8Array> {
    if (uri.scheme === 'untitled') {
      return new Uint8Array();
    }
    return workspace.fs.readFile(uri);
  }

  uri!: Uri;
  dispose(): void {
    throw new Error('Method not implemented.');
  }

  save(cancellation: CancellationToken) {

  }

  saveAs(destination: Uri, cancellation: CancellationToken) {

  }

  revert(cancellation: CancellationToken) {

  }

  async backup(destination: Uri, cancellation: CancellationToken): Promise<CustomDocumentBackup> {
    return {
      id: destination.toString(),
      delete: async () => {
        try {
          await workspace.fs.delete(destination);
        } catch {
          // noop
        }
      }
    };
  }
}

export class IBizCustomEditor implements CustomEditorProvider<IBizDocument> {

  private readonly webviews = new WebviewCollection();

  onDidChangeCustomDocument!: Event<CustomDocumentEditEvent<IBizDocument>> | Event<CustomDocumentContentChangeEvent<IBizDocument>>;


  async saveCustomDocument(document: IBizDocument, cancellation: CancellationToken): Promise<void> {
    return document.save(cancellation);
  }
  async saveCustomDocumentAs(document: IBizDocument, destination: Uri, cancellation: CancellationToken): Promise<void> {
    return document.saveAs(destination, cancellation);
  }
  async revertCustomDocument(document: IBizDocument, cancellation: CancellationToken): Promise<void> {
    return document.revert(cancellation);
  }
  async backupCustomDocument(document: IBizDocument, context: CustomDocumentBackupContext, cancellation: CancellationToken): Promise<CustomDocumentBackup> {
    return document.backup(context.destination, cancellation);
  }
  async openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Promise<IBizDocument> {
    const document: IBizDocument = await IBizDocument.create(uri, openContext.backupId, {
      getFileData: async () => {
        const webviewsForDocument = Array.from(this.webviews.get(document.uri));
        if (!webviewsForDocument.length) {
          throw new Error('Could not find webview to save for');
        }
        return new Uint8Array();
      }
    });
    return document;
  }

  resolveCustomEditor(document: IBizDocument, webviewPanel: WebviewPanel, token: CancellationToken): void | Thenable<void> {
    webviewPanel.webview.html = '打开的.ibiz文件';
  }

}

class WebviewCollection {

	private readonly _webviews = new Set<{
		readonly resource: string;
		readonly webviewPanel: WebviewPanel;
	}>();

	/**
	 * Get all known webviews for a given uri.
	 */
	public *get(uri: Uri): Iterable<WebviewPanel> {
		const key = uri.toString();
		for (const entry of this._webviews) {
			if (entry.resource === key) {
				yield entry.webviewPanel;
			}
		}
	}

	/**
	 * Add a new webview to the collection.
	 */
	public add(uri: Uri, webviewPanel: WebviewPanel) {
		const entry = { resource: uri.toString(), webviewPanel };
		this._webviews.add(entry);

		webviewPanel.onDidDispose(() => {
			this._webviews.delete(entry);
		});
	}
}
